Coverage Report

Created: 2024-12-19 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\tools.proto\tools.proto\compiler\src\gen\base\message.rs
Line
Count
Source
1
// Copyright (c) 2024, BlockProject 3D
2
//
3
// All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without modification,
6
// are permitted provided that the following conditions are met:
7
//
8
//     * Redistributions of source code must retain the above copyright notice,
9
//       this list of conditions and the following disclaimer.
10
//     * Redistributions in binary form must reproduce the above copyright notice,
11
//       this list of conditions and the following disclaimer in the documentation
12
//       and/or other materials provided with the distribution.
13
//     * Neither the name of BlockProject 3D nor the names of its contributors
14
//       may be used to endorse or promote products derived from this software
15
//       without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
use crate::compiler::message::{Field, FieldType, Message, Referenced};
30
use crate::compiler::structure::FixedFieldType;
31
use crate::compiler::util::types::TypeMapper;
32
use crate::gen::base::map::TypePathMapper;
33
use crate::gen::base::Error;
34
use crate::gen::codec::CodecMap;
35
use crate::gen::template::Template;
36
use crate::model::protocol::Endianness;
37
38
pub trait Utilities: crate::gen::base::structure::Utilities {
39
    fn get_value_type(endianness: Endianness, ty: FixedFieldType) -> &'static str;
40
    fn get_value_type_inline(endianness: Endianness, ty: FixedFieldType) -> &'static str;
41
    fn gen_struct_ref_type(type_name: &str) -> String;
42
    fn gen_message_ref_type(type_name: &str) -> String;
43
}
44
45
pub struct Templates<'fragment, 'variable> {
46
    pub template: Template<'fragment, 'variable>,
47
    pub codec_map: &'fragment CodecMap<'fragment, 'variable>,
48
}
49
50
impl<'fragment, 'variable> Templates<'fragment, 'variable> {
51
227
    pub fn get(&self, codec: &str) -> Result<&Template<'fragment, 'variable>, Error> {
52
227
        Ok(self.codec_map.get(codec).ok_or_else(|| 
Error::CodecNotFound(codec.into())0
)
?0
)
53
227
    }
54
}
55
56
49
pub fn gen_msg_field_decl<U: Utilities, T: TypeMapper>(
57
49
    field: &Field,
58
49
    templates: &Templates,
59
49
    type_path_map: &TypePathMapper<T>,
60
49
) -> Result<String, Error> {
61
49
    let codec_template = templates.get(field.codec())
?0
;
62
49
    let mut scope = templates.template.scope();
63
49
    scope.var("name", &field.name).var("description", "").var_d("info", field);
64
49
    let msg_type = match &field.ty {
65
4
        FieldType::Fixed(ty) => U::get_field_type(ty.ty).into(),
66
11
        FieldType::Ref(v) => match v {
67
9
            Referenced::Struct(v) => U::gen_struct_ref_type(&type_path_map.get(v)),
68
2
            Referenced::Message(v) => U::gen_message_ref_type(&type_path_map.get(v)),
69
        },
70
10
        FieldType::Buffer => codec_template.render("decl", &["buffer"]).map_err(Error::Codec)
?0
,
71
4
        FieldType::SizedBuffer(v) => codec_template
72
4
            .scope()
73
4
            .var("codec", U::get_value_type(field.endianness, v.ty))
74
4
            .render("decl", &["sized_buffer"])
75
4
            .map_err(Error::Codec)
?0
,
76
4
        FieldType::FixedContainer(_) => scope.render("", &["list"]).unwrap(),
77
4
        FieldType::Union(v) => scope.var("type_name", type_path_map.get(&v.r)).render("", &["union"]).unwrap(),
78
9
        FieldType::Container(_) => scope.render("", &["list"]).unwrap(),
79
0
        FieldType::Payload => codec_template.render("decl", &["payload"]).map_err(Error::Codec)?,
80
3
        FieldType::SizedContainer(_) => scope.render("", &["list"]).unwrap(),
81
    };
82
49
    let msg_type = match field.optional {
83
7
        true => codec_template.scope().var("msg_type", msg_type).render("decl", &["option"]).map_err(Error::Codec)
?0
,
84
42
        false => msg_type,
85
    };
86
49
    Ok(scope.var("type", msg_type).render("decl", &["field"]).unwrap())
87
49
}
88
89
57
fn gen_message_array_field<U: Utilities, T: TypeMapper>(
90
57
    templates: &Templates,
91
57
    function: &str,
92
57
    field: &Field,
93
57
    type_path_map: &TypePathMapper<T>,
94
57
) -> Result<Option<String>, Error> {
95
57
    let mut scope = templates.template.scope();
96
57
    let codec_template = templates.get(field.codec())
?0
;
97
57
    scope
98
57
        .var("name", &field.name)
99
57
        .var(
100
57
            "description",
101
57
            field.description.as_ref().map(U::gen_description).unwrap_or("".into()),
102
57
        )
103
57
        .var_d("info", field);
104
57
    let value = match &field.ty {
105
6
        FieldType::FixedContainer(v) => {
106
6
            scope
107
6
                .var("item_type", type_path_map.get(&v.item_type))
108
6
                .var("codec", U::get_value_type(field.endianness, v.ty));
109
6
            let type_name = codec_template
110
6
                .scope()
111
6
                .var("item_type", type_path_map.get(&v.item_type))
112
6
                .var("codec", U::get_value_type(field.endianness, v.ty))
113
6
                .render("decl", &["fixed_container"])
114
6
                .map_err(Error::Codec)
?0
;
115
6
            Some(scope.var("type_name", type_name).render(function, &["array"]).unwrap())
116
        }
117
12
        FieldType::Container(v) => {
118
12
            scope
119
12
                .var("item_type", type_path_map.get(&v.item_type))
120
12
                .var("codec", U::get_value_type(field.endianness, v.ty));
121
12
            let type_name = codec_template
122
12
                .scope()
123
12
                .var("item_type", type_path_map.get(&v.item_type))
124
12
                .var("codec", U::get_value_type(field.endianness, v.ty))
125
12
                .render("decl", &["container"])
126
12
                .map_err(Error::Codec)
?0
;
127
12
            Some(scope.var("type_name", type_name).render(function, &["list"]).unwrap())
128
        }
129
4
        FieldType::SizedContainer(v) => {
130
4
            scope
131
4
                .var("item_type", type_path_map.get(&v.item_type))
132
4
                .var("codec", U::get_value_type(field.endianness, v.ty));
133
4
            let type_name = codec_template
134
4
                .scope()
135
4
                .var("item_type", type_path_map.get(&v.item_type))
136
4
                .var("codec", U::get_value_type(field.endianness, v.ty))
137
4
                .var("size_codec", U::get_value_type(field.endianness, v.size_ty))
138
4
                .render("decl", &["sized_container"])
139
4
                .map_err(Error::Codec)
?0
;
140
4
            Some(scope.var("type_name", type_name).render(function, &["list"]).unwrap())
141
        }
142
35
        _ => None,
143
    };
144
57
    Ok(value)
145
57
}
146
147
34
pub fn gen_message_array_type_decls<U: Utilities, T: TypeMapper>(
148
34
    templates: &Templates,
149
34
    function: &str,
150
34
    msg: &Message,
151
34
    type_path_map: &TypePathMapper<T>,
152
34
) -> Result<String, Error> {
153
34
    msg.fields
154
34
        .iter()
155
57
        .filter_map(|field| gen_message_array_field::<U, T>(templates, function, field, type_path_map).transpose())
156
34
        .collect::<Result<Vec<String>, Error>>()
157
34
        .map(|v| v.join(""))
158
34
}
159
160
28
pub fn generate<'variable, U: Utilities, T: TypeMapper>(
161
28
    mut templates: Templates<'_, 'variable>,
162
28
    msg: &'variable Message,
163
28
    type_path_map: &TypePathMapper<T>,
164
28
) -> Result<String, Error> {
165
28
    templates.template.var("msg_name", &msg.name);
166
28
    templates.template.var(
167
28
        "msg_description",
168
28
        msg.description.as_ref().map(U::gen_description).unwrap_or("".into()),
169
28
    );
170
28
    let fields = msg
171
28
        .fields
172
28
        .iter()
173
49
        .map(|v| gen_msg_field_decl::<U, T>(v, &templates, type_path_map))
174
28
        .collect::<Result<Vec<String>, Error>>()
?0
175
28
        .join("");
176
28
    let mut code = templates.template.scope().var("fields", fields).render("", &["decl"]).unwrap();
177
28
    code += "\n";
178
28
    code += &gen_message_array_type_decls::<U, T>(&templates, "typealias", msg, type_path_map)
?0
;
179
28
    Ok(code)
180
28
}